// Copyright 2019 The rhyme_lph Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// M [author] rhyme_lph
// b [email]  rhymelph@gmail.com
// e [github] https://github.com/rhymelph

import 'package:base_plugin/base_plugin.dart';
import 'package:finance_app/generated/l10n.dart';
import 'package:finance_app/src/common/config.dart';
import 'package:finance_app/src/entities/inventory_entity.dart';
import 'package:finance_app/src/entities/ocr_attachment_entity.dart';
import 'package:finance_app/src/entities/product_entity.dart';
import 'package:finance_app/src/entities/setting_data_entity.dart';
import 'package:finance_app/src/entities/transaction_entity.dart';
import 'package:finance_app/src/http_service/http_attachment_service.dart';
import 'package:finance_app/src/http_service/http_coa_service.dart';
import 'package:finance_app/src/http_service/http_inventory_service.dart';
import 'package:finance_app/src/http_service/http_product_service.dart';
import 'package:finance_app/src/mixin/check_plan_mixin.dart';
import 'package:finance_app/src/mixin/from_attach_mixin.dart';
import 'package:finance_app/src/mixin/init_edit_data_mixin.dart';
import 'package:finance_app/src/provider/dashboard/products_services/products_and_services_provider.dart';
import 'package:finance_app/src/provider/dashboard/products_services/views/products_provider.dart';
import 'package:finance_app/src/router_gen/router_gen_providers.dart';
import 'package:finance_app/src/utils/cash_utils.dart';
import 'package:finance_app/src/widgets/bottom_sheet/currency_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/delete_info_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/got_it_tip_bottom_sheet.dart';
import 'package:finance_app/src/widgets/bottom_sheet/list_select_bottom_sheet.dart';
import 'package:flutter/material.dart';

class NewProductProvider extends BaseProvider with FormAttachMixin, InitEditDataMixin, CheckPlanMixin {
  FocusNode nameFocusNode = FocusNode();
  TextEditingController nameController = TextEditingController();

  FocusNode unitPriceFocusNode = FocusNode();
  TextEditingController unitPriceController = TextEditingController();

  TextEditingController quantityController = TextEditingController();
  FocusNode quantityFocusNode = FocusNode();

  TextEditingController purchasePriceController = TextEditingController();
  FocusNode purchasePriceFocusNode = FocusNode();

  TextEditingController productDescController = TextEditingController();
  FocusNode productDescFocusNode = FocusNode();

  TextEditingController barCodeController = TextEditingController();
  FocusNode barCodeFocusNode = FocusNode();

  TextEditingController codeController = TextEditingController();
  FocusNode codeFocusNode = FocusNode();

  TextEditingController productQuantityController = TextEditingController();
  FocusNode productQuantityFocusNode = FocusNode();

  bool canEditInventory = true;

  SettingDataUnitType unit;

  List<String> menuList = ['Edit', 'Delete', 'Deactivate', 'Adjust Inventory'];

  List<InventoryEntity> inventoryList;

  bool get enable => unitPriceController.text.length > 0;

  bool get showSellPrice => id == null || isEdit || enableSell;

  bool get showBuyPrice => id == null || isEdit || enableBuy;

  bool get enableAdd =>
      unitPriceController.text.length > 0 &&
      unit != null &&
      nameController.text.length > 0 &&
      quantityController.text.length > 0 &&
      purchasePriceController.text.length > 0;

  @override
  Future get dataFuture => null;

  bool isCheckInclusive = false;

  bool isActivate;

  @override
  void dispose() {
    super.dispose();
    codeFocusNode.dispose();
    codeController.dispose();
    nameFocusNode.dispose();
    nameController.dispose();
    unitPriceFocusNode.dispose();
    unitPriceController.dispose();
    quantityFocusNode.dispose();
    quantityController.dispose();
    barCodeFocusNode.dispose();
    barCodeController.dispose();
    productDescFocusNode.dispose();
    productDescController.dispose();
    sellFocusNode.dispose();
    sellPriceController.dispose();
    buyFocusNode.dispose();
    buyPriceController.dispose();
  }

  void unFocus() {
    codeFocusNode.unfocus();
    nameFocusNode.unfocus();
    unitPriceFocusNode.unfocus();
    quantityFocusNode.unfocus();
    barCodeFocusNode.unfocus();
    productDescFocusNode.unfocus();
    sellFocusNode.unfocus();
    buyFocusNode.unfocus();
  }

  @override
  void initState() {
    super.initState();
    unitPriceController.addListener(() {
      update();
    });
    nameController.addListener(() {
      update();
    });
    quantityController.addListener(() {
      update();
    });

    purchasePriceController.addListener(() {
      update();
    });
    if (id != null && inventoryEnable == true) {
      _loadInventoryList();
    }
  }

  void onChangeInclusive() {
    unFocus();
    isCheckInclusive = !isCheckInclusive;
    update();
  }

  void onChangeProductUnit() {
    unFocus();
    obtainContext?.call((context) async {
      unit = await showUnitOfMeasurementBottomSheet(context, unit);
      update();
    });
  }

  /// ***************** Pro *****************  ///
  List<String> categories;

  bool enableSell = false;
  bool isCheckSellInclusive = true;
  SettingDataCurrency sellCurrency;
  SettingDataTax sellGstRate;
  TextEditingController sellPriceController = TextEditingController();
  FocusNode sellFocusNode = FocusNode();

  bool enableBuy = false;
  bool isCheckBuyInclusive = true;
  SettingDataCurrency buyCurrency;
  SettingDataTax buyGstRate;
  TextEditingController buyPriceController = TextEditingController();
  FocusNode buyFocusNode = FocusNode();

  List<ProductBoms> bomsList = [];

  bool inventoryEnable = false;

  void onChangeSell() {
    unFocus();
    enableSell = !enableSell;
    if (id == null && enableSell == true) {
      sellGstRate = Config.settingInfo.taxData;
    }
    update();
  }

  void onChangeSellInclusive() {
    unFocus();
    isCheckSellInclusive = !isCheckSellInclusive;
    update();
  }

  void onSelectCategory() async {
    unFocus();
    final result = await Routes.navigateTo(RouterGenProviders.productsAndServicesCategoryPage, params: {
      'type': 'product',
      'categories': categories,
    });
    if (result != null) {
      categories = result;
      update();
    }
  }

  void onChangeSellUnit() {
    unFocus();
    obtainContext?.call((context) async {
      sellCurrency = await showCurrencyBottomSheet(context, sellCurrency);
      update();
    });
  }

  void onChangeSellGstRate() {
    unFocus();
    obtainContext?.call((context) async {
      sellGstRate = await showGstRateBottomSheet(context, sellGstRate);
      update();
    });
  }

  void onChangeBuy() {
    unFocus();

    enableBuy = !enableBuy;
    if (id == null && enableBuy == true) {
      buyGstRate = Config.settingInfo.taxData;
    }
    update();
  }

  void onChangeBuyInclusive() {
    unFocus();
    isCheckBuyInclusive = !isCheckBuyInclusive;
    update();
  }

  void onChangeBuyUnit() {
    unFocus();
    obtainContext?.call((context) async {
      buyCurrency = await showCurrencyBottomSheet(context, buyCurrency);
      update();
    });
  }

  void onChangeBuyGstRate() {
    unFocus();
    obtainContext?.call((context) async {
      buyGstRate = await showGstRateBottomSheet(context, buyGstRate);
      update();
    });
  }

  void onSave() async {
    unFocus();
    if (codeController.text.length == 0) {
      showToast(S.current.please_enter_the_product_code);
      return;
    }

    if (nameController.text.length == 0) {
      showToast(S.current.please_enter_the_product_name);
      return;
    }
    if (unit == null) {
      showToast(S.current.please_select_unit_of_measurement);
      return;
    }
    double sellPrice = 0.0;
    if (enableSell) {
      sellPrice = double.parse(sellPriceController.text, (e) => null);
      if (sellPrice == null) {
        showToast(S.current.please_enter_the_real_sell_price);
        return;
      }
      if (sellGstRate == null && Config.currentCompany.enableGst != 1) {
        sellGstRate = Config.settingInfo.taxData ?? SettingDataTax();
      }
      if (sellGstRate == null) {
        showToast(S.current.please_select_sell_gst_rate);
        return;
      }
    }

    double buyPrice = 0.0;

    if (enableBuy) {
      buyPrice = double.parse(buyPriceController.text, (e) => null);
      if (buyPrice == null) {
        showToast(S.current.please_enter_the_real_buy_price);
        return;
      }
      if (buyGstRate == null && Config.currentCompany.enableGst != 1) {
        buyGstRate = Config.settingInfo.taxData ?? SettingDataTax();
      }
      if (buyGstRate == null) {
        showToast(S.current.please_select_buy_gst_rate);
        return;
      }
    }
    int quantity = int.parse(productQuantityController.text, onError: (e) => null);
    if (buyPrice == null) {
      showToast(S.current.please_enter_the_real_quantity);
      return;
    }

    if (quantity != null && quantity >= 1 && (buyPrice == null || buyPrice == 0.0)) {
      showToast(S.current.please_enter_the_real_quantity);
      return;
    }

    // 产品 boms
    List boms = [];
    for (final item in bomsList) {
      boms.add({
        'material_id': item.id,
        'quantity': bomsMap[item.id] ?? 1,
      });
    }

    if (boms.length > 0 && productQuantityController.text.isEmpty) {
      showToast(S.current.please_enter_initial_product_quantity);
      productQuantityFocusNode.requestFocus();
      return;
    }
    bool hasSetInventoryBalance;
    await post(HttpCoaService.hasSetOpenningBalance(), onSuccess: (result) {
      hasSetInventoryBalance = result.data['data']['open_balance'] > 0;
    });
    obtainContext?.call((context) async {
      bool isSuccess = true;
      if (hasSetInventoryBalance && canEditInventory && (quantity ?? 0) > 0) {
        isSuccess = false;

        isSuccess = await showInitialProductQuantityBottomSheet(context);
      }

      if (isSuccess) {
        List<int> attachmentIds = [];
        obtainContext?.call((context) {
          BaseConfig.dialogBase.showLoadingDialog(context: context, text: S.current.loading);
        });
        if (files.length > 0) {
          for (final item in files) {
            await post(
                HttpAttachmentService.createAttachment(
                  item,
                ), onSuccess: (result) {
              final data = getEntityFromResult<OcrAttachmentEntity>(result);
              attachmentIds.add(data.id);
            });
          }
        }
        if (attachmentIds.length != files.length) {
          showToast(S.current.file_upload_failure);
          obtainContext?.call((context) {
            Navigator.of(context).pop();
          });
          return;
        }
        ProductEntity entity;
        String errorMsg;
        if (isEdit == true) {
          await post(
              HttpProductService.productUpdate(
                  id,
                  null,
                  nameController.text,
                  codeController.text,
                  barCodeController.text,
                  unit?.key,
                  productDescController.text,
                  enableSell ? 1 : 0,
                  sellCurrency?.key,
                  sellPrice,
                  sellGstRate?.id,
                  isCheckSellInclusive ? 1 : 0,
                  enableBuy ? 1 : 0,
                  buyCurrency?.key,
                  buyPrice,
                  buyGstRate?.id,
                  isCheckBuyInclusive ? 1 : 0,
                  inventoryEnable ? 1 : 0,
                  quantity,
                  boms,
                  attachmentIds,
                  categories), onSuccess: (result) {
            entity = getEntityFromResult<ProductEntity>(result);
          }, onError: (int code, String msg, Object error, _) {
            errorMsg = msg;
          });
        } else {
          await post(
              HttpProductService.productCreate(
                  null,
                  nameController.text,
                  codeController.text,
                  barCodeController.text,
                  unit?.key,
                  productDescController.text,
                  enableSell ? 1 : 0,
                  sellCurrency?.key,
                  sellPrice,
                  sellGstRate?.id,
                  isCheckSellInclusive ? 1 : 0,
                  enableBuy ? 1 : 0,
                  buyCurrency?.key,
                  buyPrice,
                  buyGstRate?.id,
                  isCheckBuyInclusive ? 1 : 0,
                  inventoryEnable ? 1 : 0,
                  quantity,
                  boms,
                  attachmentIds,
                  categories), onSuccess: (result) {
            entity = getEntityFromResult<ProductEntity>(result);
          }, onError: (int code, String msg, Object error, _) {
            errorMsg = msg;
          });
        }
        obtainContext?.call((context) {
          Navigator.of(context).pop();
        });
        if (entity != null) {
          final provider = Providers.get<ProductsAndServicesProvider>(RouterGenProviders.productsAndServicesPage, null);
          if (provider != null) {
            provider.getChildProvider<ProductsProvider>().updateProduct(entity);
          }

          if (entity != null) {
            obtainContext?.call((context) {
              Navigator.of(context).pop(entity);
            });
          }
        } else {
          obtainContext?.call((context) {
            showGotItTipBottomSheet(context, errorMsg);
          });
        }
      }
    });
  }

  void onCameraBarcode() async {
    final result = await Routes.navigateTo(RouterGenProviders.barCodeCameraPage);
    if (result != null && result is String) {
      barCodeController.text = result;
    }
  }

  void onAddBillOfMaterial() async {
    final result = await Routes.navigateTo(RouterGenProviders.productBomListPage, params: {'filterData': bomsList});
    if (result != null) {
      bomsList.add(result);
      update();
    }
  }

  void onChangeInventoryEnable() {
    if (!Config.canAccessAdvanceFeature) {
      obtainContext?.call((context) {
        showReadyForUpgradeBottomSheet(context);
      });
      return;
    }
    inventoryEnable = !inventoryEnable;
    update();
  }

  Map<int, int> bomsMap = {};

  void onUpdateBomNumber(int id, int data) {
    if (data == 0) {
      bomsMap.remove(id);
      bomsList.removeWhere((element) => element.id == id);
      update();
    } else {
      bomsMap[id] = data;
    }
  }

  void initProduct(ProductEntity data, [String text]) async {
    if (text != null) {
      nameController.text = text;
    }
    if (data == null) {
      buyCurrency = Config.defaultCurrency;
      sellCurrency = Config.defaultCurrency;
      return;
    }
    init(data.id);
    isActivate = data.isActivated == 1;
    codeController.text = data.code;
    nameController.text = data.name;
    unit = Config.settingData.unitType.firstWhere((element) => element.key == data.unitType, orElse: () => null);
    categories = data.categories;
    productDescController.text = data.description;
    barCodeController.text = data.barcode;
    canEditInventory = data.hasInventory == 0;
    enableSell = data.enableSell == 1;
    if (enableSell == true) {
      sellCurrency = Config.settingData.currency.firstWhere((element) => element.key == data.currencyCode, orElse: () => null);
      sellPriceController.text = data.price?.toString();
      sellGstRate = data.tax;
      isCheckSellInclusive = data.isTaxInclusive == 1;
    }
    enableBuy = data.enablePurchase == 1;
    if (enableBuy) {
      buyCurrency = Config.settingData.currency.firstWhere((element) => element.key == data.purchaseCurrencyCode, orElse: () => null);
      buyPriceController.text = data.purchasePrice?.toString();
      buyGstRate = data.purchaseTax;
      isCheckBuyInclusive = data.purchaseIsTaxInclusive == 1;
    }
    inventoryEnable = data.enableInventory == 1;
    if (inventoryEnable) {
      productQuantityController.text = data.inventoryLeft?.toString();
      bomsList = data.boms ?? [];
      if (bomsList != null) {
        for (final item in bomsList) {
          bomsMap[item.id] = item.pivot?.quantity ?? 1;
        }
      }
    }
    if (data.isActivated == 1) {
      //显示禁用的菜单
      if (data.enableInventory == 1) {
        menuList = ['Edit', 'Delete', 'Deactivate', 'Adjust Inventory'];
      } else {
        menuList = [
          'Edit', 'Delete', 'Deactivate'
          // , 'Adjust Inventory'
        ];
      }
    } else {
      menuList = ['Delete', 'Activate'];
    }
    attachments = data.attachments;
  }

  void onSelectMenu(String value) {
    switch (value) {
      case 'Edit':
        onChangeEditMode();
        break;
      case 'Delete':
        onDelete();
        break;
      case 'Deactivate':
        onDeactivate();
        break;
      case 'Activate':
        onActivate();
        break;
      case 'Adjust Inventory':
        onAdjustInventory();
        break;
    }
  }

  void onActivate() {
    if (isPlanExpired()) return;

    obtainContext?.call((context) async {
      ProductEntity entity;
      await post(HttpProductService.activateProduct(id), autoHandler: AutoHandler.Toast, loadingTip: S.current.loading, onSuccess: (result) {
        entity = getEntityFromResult<ProductEntity>(result);
      });
      if (entity != null) {
        obtainContext?.call((context) {
          showProductActivatedSuccessBottomSheet(context);
        });
        initProduct(entity);
        update();
        Providers.get<ProductsAndServicesProvider>(RouterGenProviders.productsAndServicesPage, null)
            ?.getChildProvider<ProductsProvider>()
            ?.batchUpdate([entity]);
      }
    });
  }

  void onDeactivate() {
    if (isPlanExpired()) return;

    obtainContext?.call((context) async {
      bool isSuccess = await showProductDeactivateInfoBottomSheet(context);
      if (isSuccess == true) {
        ProductEntity entity;
        await post(HttpProductService.deactivateProduct(id), autoHandler: AutoHandler.Toast, loadingTip: S.current.loading, onSuccess: (result) {
          entity = getEntityFromResult<ProductEntity>(result);
        });
        if (entity != null) {
          initProduct(entity);
          update();
          await showProductDeActivatedSuccessBottomSheet(context);
          Providers.get<ProductsAndServicesProvider>(RouterGenProviders.productsAndServicesPage, null)
              ?.getChildProvider<ProductsProvider>()
              ?.batchUpdate([entity]);
        }
      }
    });
  }

  void onDelete() {
    if (isPlanExpired()) return;

    obtainContext?.call((context) async {
      bool isSuccess = await showProductDeleteInfoBottomSheet(context);
      if (isSuccess == true) {
        isSuccess = false;
        await post(HttpProductService.deleteProduct(id), autoHandler: AutoHandler.Toast, loadingTip: S.current.loading, onSuccess: (result) {
          isSuccess = true;
        });
        if (isSuccess) {
          await showProductDeleteSuccessBottomSheet(context);
          Navigator.of(context).pop({'delete': true});
        }
      }
    });
  }

  void onAdjustInventory() async {
    if (isPlanExpired()) return;

    final result = await Routes.navigateTo(RouterGenProviders.adjustInventoryPage, params: {
      'id': id,
      'quantity': productQuantityController.text,
      'productName': nameController.text,
    });
    if (result != null) {
      initProduct(result);
      _loadInventoryList();
      Providers.get<ProductsAndServicesProvider>(RouterGenProviders.productsAndServicesPage, null)?.getChildProvider<ProductsProvider>()?.updateProduct(result);
      update();
    }
  }

  void _loadInventoryList() async {
    await post(HttpInventoryService.getInventoryList(id), autoHandler: AutoHandler.Toast, onSuccess: (result) {
      inventoryList = getEntityFromResult<List<InventoryEntity>>(result);
    });
    if (inventoryList == null) {
      inventoryList = [];
    }
    update();
  }

  void onNavInventoryItem(InventoryEntity e) async {
    if (e.name == 'Initial Inventory') {
      return;
    } else if (e.type == 'invoice') {
      await Routes.navigateTo(RouterGenProviders.newInvoicePage, params: {"data": TransactionEntity()..transactionableId = int.parse(e.typeId.toString())});
    } else if (e.type == 'bill') {
      await Routes.navigateTo(RouterGenProviders.newBillPage, params: {"data": TransactionEntity()..transactionableId = int.parse(e.typeId.toString())});
    } else {
      await Routes.navigateTo(RouterGenProviders.inventoryAdjustmentPage, params: {
        'data': e,
        'batchId': e.batch,
      });
    }
  }

  @override
  List<OcrAttachmentEntity> attachments;

  @override
  Future get editDataFuture => dataFuture;

  @override
  TextEditingController get numberCtl => codeController;

  @override
  String get type => 'product';

  void onChangeBom(int index) async {
    final result = await Routes.navigateTo(RouterGenProviders.productBomListPage, params: {
      'select': bomsList[index],
      'filterData': bomsList,
    });
    if (result != null) {
      bomsMap.remove(bomsList[index].id);
      bomsList.removeAt(index);
      bomsList.insert(index, result);
      bomsMap[bomsList[index].id] = 1;
      update();
    }
  }
}
